﻿/*****************************************************
通用插件合集:

Module name:usefulPugins.js
Author:悦来客栈的老板
Date:2022.12.22
Version:V1.5.1


欢迎加入本人的星球:


https://t.zsxq.com/FMRf2ZV

本人微信:523176585

*****************************************************/

const types      = require("@babel/types");




function isNodePure(node,scope)
{
	if (types.isLiteral(node))
	{
		return true;
	}
	
	if (types.isUnaryExpression(node))
	{
		return isNodePure(node.argument,scope)
	}
	
	if (types.isIdentifier(node))
	{//处理 var c = String;
		if (scope && scope.isPure(node,true))
		{
			return true;
		}
		
		if (typeof this[node.name] != 'undefined')
		{
			return true;
		}
		
		return false;
	}
	
	if (types.isMemberExpression(node))
	{//处理 var d = String.fromCharCode;
		
		let {object,property,computed} = node;
		
		if (computed && !isNodePure(property,scope))
		{
			return false;
		}
		if (isNodePure(object,scope))
		{
			return true;
		}

		if (types.isIdentifier(object) )
		{

			let name = object.name;
			
			if (typeof this[name] != 'undefined' && name != 'window')
			{//注意object为window时，可能会还原出错
				return true;
			}
			
			return false;
		}
		if(types.isMemberExpression(object))
		{
			return isNodePure(object,scope);
		}
			
		return false;
	}
	
	if(types.isBinary(node) && scope)
	{
		return isNodePure(node.left,scope) && isNodePure(node.right,scope);
	}
	
	return false;
}


//判断节点是否为字面量，插件地址 https://t.zsxq.com/09CvEE1FY
function isNodeLiteral(node) {
    if (Array.isArray(node)) {
        return node.every(ele => isNodeLiteral(ele));
    }
    
    if (types.isThisExpression(node))
    {
    	return true;
    }
    
    if (types.isLiteral(node)) {
    	if (node.value == null)
    	{
    		return false;
    	}
      return true;
    }
    if(types.isBinaryExpression(node))
    {
    	return isNodeLiteral(node.left) && isNodeLiteral(node.right);
    }
    if (types.isUnaryExpression(node, {
        "operator": "-"
    }) || types.isUnaryExpression(node, {
        "operator": "+"
    })) {
        return isNodeLiteral(node.argument);
    }

    if (types.isObjectExpression(node)) {
        let { properties } = node;
        if (properties.length == 0) {
            return true;
        }

        return properties.every(property => isNodeLiteral(property));

    }
    if (types.isArrayExpression(node)) {
        let { elements } = node;
        if (elements.length == 0) {
            return true;
        }
        return elements.every(element => isNodeLiteral(element));
    }

    return false;
}









//规范For循环和While循环
const standardLoop =
{
    "ForStatement|WhileStatement|ForInStatement|ForOfStatement"({ node }) {
        if (!types.isBlockStatement(node.body)) {
            node.body = types.BlockStatement([node.body]);
        }
    },
    "IfStatement"(path)
    {
    	const consequent = path.get("consequent");
    	const alternate  = path.get("alternate");
    	if (!consequent.isBlockStatement()) 
    	{
    		consequent.replaceWith(types.BlockStatement([consequent.node]));
    	}
    	if (alternate.node !== null && !alternate.isBlockStatement()) {
    		alternate.replaceWith(types.BlockStatement([alternate.node]));
    	}
    },
}

function SequenceOfStatement(path) {
	let { scope, parentPath, node } = path;
	let expressions = node.expressions;
	if (parentPath.isExpressionStatement({ "expression": node })) {
		parentPath.node.expression = expressions.pop();
	}
	else {
		return;
	}
	for (let expression of expressions) {
		parentPath.insertBefore(types.ExpressionStatement(expression = expression));
	}
}





const resolveSequence =
{
	SequenceExpression:
	{//对同一节点遍历多个方法
		exit: [SequenceOfStatement]
	}
}

const simplifyLiteral = {
	NumericLiteral({node}) {
		if (node.extra && /^0[obx]/i.test(node.extra.raw)) {
			node.extra = undefined;
		}
  },
  StringLiteral({node}) 
  {
  	if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
  		node.extra = undefined;
    }
  },
}

const constantFold = {
    "BinaryExpression|UnaryExpression"(path) {
        if(path.isUnaryExpression({operator:"-"}) || 
    	   path.isUnaryExpression({operator:"void"}))
    	{
    		return;
    	}
        const {confident, value} = path.evaluate();
        if (!confident)
            return;
        if (typeof value == 'number' && (!Number.isFinite(value))) {
            return;
        }
        path.replaceWith(types.valueToNode(value));
    },
}


global.constantFold            = constantFold;
global.standardLoop            = standardLoop;
global.isNodeLiteral           = isNodeLiteral
global.simplifyLiteral         = simplifyLiteral;
global.resolveSequence         = resolveSequence;





